package com.lin.netty.chat.protocol;

import com.lin.netty.chat.Config;
import com.lin.netty.chat.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteOrder;
import java.util.List;

/**
 * 消息编解码器
 * <p>
 * 自定义通信协议，客户端和服务器必须保证互发送消息的格式一致
 * <p>
 * 自定义协议的要素：
 * * 魔数，用来在第一时间判定是否是无效数据包
 * * 版本号，可以支持协议的升级
 * * 序列化算法，消息正文到底采用哪种序列化反序列化方式，可以由此扩展，例如：json、protobuf、hessian、jdk
 * * 消息类型，是登录、注册、单聊、群聊... 跟业务相关
 * * 请求序号，为了双工通信，提供异步能力
 * * 正文长度
 * * 消息正文
 */
@Slf4j
public class MessageCodec extends MessageToMessageCodec<ByteBuf, Message> {
    /**
     * 消息编码器(消息出站处理器)
     *
     * @param ctx
     * @param msg     要写出去的消息
     * @param outList 处理好的消息，调用outList.add则传给下一个出站处理器
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        ByteBuf out = ctx.alloc().buffer();

        // 1 魔数(4字节)
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2 版本号(1字节)
        out.writeByte(0);
        // 3 序列化算法(1字节)
        out.writeByte(Config.getSerializerAlgorithm().ordinal());
        // 4 消息类型(1字节)
        out.writeByte(msg.getMessageType());
        // 5 请求序号(4字节)
        out.writeInt(msg.getSequenceId());

        // 将消息序列化，才能在网络中传输
        byte[] bytes = Config.getSerializerAlgorithm().serialize(msg);
        // 6 正文长度(4字节)
        out.writeInt(bytes.length);
        // 无意义1字节，补充长度到2的n次方
        out.writeByte(0);

        // 7 正文内容
        out.writeBytes(bytes);

        outList.add(out); // 传给下一个出站handler
    }

    /**
     * 消息解码器(消息入站处理器)
     *
     * @param ctx
     * @param in  客户端消息
     * @param out 处理好的消息，调用out.add则传给下一个入站处理器
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        log.debug("消息解码器(消息入站处理器)");
        int magicNum = in.readInt();
        byte version = in.readByte();
        byte serializerAlo = in.readByte();
        byte messageType = in.readByte();
        int requestNum = in.readInt();
        int contentLen = in.readInt();
        in.readByte();
        byte[] bytes = new byte[contentLen];
        in.readBytes(bytes, 0, contentLen);
        // 将字节反序列化成消息
        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlo];
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        Message message = algorithm.deserialize(messageClass, bytes);

        log.debug("{},{},{},{},{},{}", magicNum, version, serializerAlo, messageType, requestNum, contentLen);
        log.debug("{}", message);
        out.add(message); // 传给下一个入站处理器
    }
}
